#include <map>
#include <iostream>
#include <fstream>
using namespace std;
#include "mem/mo_hash.h"
#include "utils/time.h"
using namespace tdl;
#include "wbl_smap_ex.h"
using namespace wbl;

struct uinfo
{
	char		lc[16];
	uint8_t		iplen;
	char		cip[15];
public:
	uinfo() : iplen(0)
	{
		memset(lc, 0, 16);
		memset(cip, 0, 15);
	}
	uinfo(const string& lcc, const string& ip)
	{
		memcpy(lc, lcc.c_str(), 16);
		iplen = ip.length();
		memset(cip, 0, 15);
		memcpy(cip, ip.c_str(), iplen);
	}

	void operator=(const uinfo& ui)
	{
		memcpy(lc, ui.lc, 16);
		iplen = ui.iplen;
		memcpy(cip, ui.cip, 15);
	}
};

//////////////////////////
key_t shmkey = 0x6081524;
uint32_t n_insert = 0, n_touch = 0, n_find = 0, n_get = 0, n_erase = 0;
usec_t s, e, time_insert = 0, time_touch = 0, time_find = 0, time_gets = 0, time_erase = 0;

///////////////////////////
uint32_t parse_line(string& sline, string& lc, string& ip)
{
	int pos = sline.find_first_of('|');
	string qq = sline.substr(0, pos);
	sline = sline.substr(pos + 1);
	pos = sline.find_first_of('|');
	lc = sline.substr(0, pos);
	ip = sline.substr(pos + 1);

	uint32_t key = atoi(qq.c_str());	
	return key;	
}

string parse_line2(string& sline, string& lc, string& ip)
{
	int pos = sline.find_first_of('|');
	string qq = sline.substr(0, pos);
	sline = sline.substr(pos + 1);
	pos = sline.find_first_of('|');
	lc = sline.substr(0, pos);
	ip = sline.substr(pos + 1);

	return qq;	
}

void print_into_statfile(ofstream& ofs, char* prefix, usec_t& t, uint32_t n)
{
	ofs << prefix << (double)t / (double)n << "(us)" << endl;
}

////////////////////////
template <typename T>
void test_moh_op(T& moh)
{
	typedef typename T::dn_type dn_type;
	typedef typename T::pair_type pair_type;

	ifstream ifs;
	string sline, lc, ip;
	ifs.open("moh_iq.dat");
	while(getline(ifs, sline))
	{
		uint32_t key = parse_line(sline, lc, ip);
		uinfo ui(lc, ip);

		s = get_usec(get_now_utime());
		moh.insert(key, &ui);
		e = get_usec(get_now_utime());
		n_insert++;
		time_insert += (e - s);

		s = get_usec(get_now_utime());
		pair_type pair = moh.find(key);
		e = get_usec(get_now_utime());
		n_find++;
		time_find += (e - s);

		if(pair.second)
		{
			dn_type* pdn = pair.first;
			s = get_usec(get_now_utime());
			moh.touch(pdn);
			e = get_usec(get_now_utime());
			n_touch++;
			time_touch += (e - s);
		}
	
		s = get_usec(get_now_utime());
		pair = moh.get(key);
		e = get_usec(get_now_utime());
		n_get++;
		time_gets += (e - s);

		if(pair.second)
		{
			dn_type* pdn = pair.first;
			s = get_usec(get_now_utime());
			moh.erase(pdn);
			e = get_usec(get_now_utime());
			n_erase++;
			time_erase += (e - s);
		}
	}
	ifs.close();
}

template <typename T>
void fill_moh(T& moh)
{
	typedef typename T::dn_type dn_type;
	typedef typename T::pair_type pair_type;

	ifstream ifs;
	string sline, lc, ip;
	ifs.open("moh_org.dat");
	while(getline(ifs, sline))
	{
		uint32_t key = parse_line(sline, lc, ip);
		uinfo ui(lc, ip);

		pair_type pair = moh.find(key);
		if(! pair.second)
			moh.insert(key, &ui);
		else
		{
			dn_type* pdn = pair.first;
			pdn->value() = ui;
			moh.touch(pdn);
		}
	}
	ifs.close();
}

template <typename T>
void test_moh(T& moh, char* prefix)
{
	try {
		if(! moh.initialize(15000000, shmkey++))
		{
			fill_moh(moh);
			cout << "fill moh ok" << endl;
		}
	} catch(tdl::mohash_ex& e) {
		cerr << e.what_hell() << endl;
		return;
	}

	n_insert = n_touch = n_find = n_get = n_erase = 0;
	time_insert = time_touch = time_find = time_gets = time_erase = 0;

	test_moh_op(moh);

	ofstream ofs;
	ofs.open("moh_stat.dat", ios::out | ios::app);
	ofs << "=== " << prefix << " ===" << endl;
	print_into_statfile(ofs, "insert(): ", time_insert, n_insert);
	print_into_statfile(ofs, "find(): ", time_find, n_find);
	print_into_statfile(ofs, "touch():", time_touch, n_touch);
	print_into_statfile(ofs, "get(): ", time_gets, n_get);
	print_into_statfile(ofs, "erase(): ", time_erase, n_erase);
	ofs.close();

	// delete shm
	::shmctl(moh.get_mem().id, IPC_RMID, NULL);
	
	cout << "test ok" << endl;
}

//////////////////
void fill_map(std::map<uint32_t, uinfo>& m)
{
	ifstream ifs;
	string sline, lc, ip;
	ifs.open("moh_org.dat");
	while(getline(ifs, sline))
	{
		uint32_t key = parse_line(sline, lc, ip);
		uinfo ui(lc, ip);

		std::map<uint32_t, uinfo>::iterator it = m.find(key);
		if(it == m.end())
			m.insert(make_pair(key, ui));
		else
		{
			uinfo& uii = it->second;
			uii = ui;
		}
	}
	ifs.close();
	cout << "fill map ok" << endl;
}

void test_map_op(std::map<uint32_t, uinfo>& m)
{
	ifstream ifs;
	string sline, lc, ip;
	ifs.open("moh_iq.dat");
	while(getline(ifs, sline))
	{
		uint32_t key = parse_line(sline, lc, ip);
		uinfo ui(lc, ip);

		s = get_usec(get_now_utime());
		m.insert(make_pair(key, ui));
		e = get_usec(get_now_utime());
		n_insert++;
		time_insert += (e - s);

		s = get_usec(get_now_utime());
		std::map<uint32_t, uinfo>::iterator it = m.find(key);
		e = get_usec(get_now_utime());
		n_find++;
		time_find += (e - s);

		s = get_usec(get_now_utime());
		m.erase(it);
		e = get_usec(get_now_utime());
		n_erase++;
		time_erase += (e - s);
	}
	ifs.close();
}

void test_map()
{
	typedef std::map<uint32_t, uinfo> map_type;

	n_insert = n_touch = n_find = n_get = n_erase = 0;
	time_insert = time_touch = time_find = time_gets = time_erase = 0;

	map_type m;

	fill_map(m);

	test_map_op(m);

	ofstream ofs;
	ofs.open("moh_stat.dat", ios::out | ios::app);
	ofs << "=== std::map ===" << endl;
	print_into_statfile(ofs, "insert(): ", time_insert, n_insert);
	print_into_statfile(ofs, "find(): ", time_find, n_find);
	print_into_statfile(ofs, "erase(): ", time_erase, n_erase);
	ofs.close();

	m.clear();
	
	cout << "test ok" << endl;
}

/////////////////////
void fill_smap(wbl::smap_ex& smap)
{
	ifstream ifs;
	string sline, lc, ip;
	ifs.open("moh_org.dat");
	while(getline(ifs, sline))
	{
		string key = parse_line2(sline, lc, ip);
		uinfo ui(lc, ip);

		uint32_t uilen = sizeof(uinfo);
		smap.set(key, (char*)&ui, uilen);
	}
	ifs.close();
	cout << "fill smap ok" << endl;
}

void test_smap_op(wbl::smap_ex& smap)
{
	ifstream ifs;
	string sline, lc, ip;
	ifs.open("moh_iq.dat");
	while(getline(ifs, sline))
	{
		string key = parse_line2(sline, lc, ip);
		uinfo ui(lc, ip);

		uint32_t uilen = sizeof(uinfo);
		s = get_usec(get_now_utime());
		smap.set(key, (char*)&ui, uilen);
		e = get_usec(get_now_utime());
		n_insert++;
		time_insert += (e - s);

		s = get_usec(get_now_utime());
		bool r = smap.find(key);
		e = get_usec(get_now_utime());
		n_find++;
		time_find += (e - s);

		if(r)
		{
			s = get_usec(get_now_utime());
			smap.touch_access(key);
			e = get_usec(get_now_utime());
			n_touch++;
			time_touch += (e - s);
		}

		uinfo uii;
		uilen = sizeof(uinfo);
		s = get_usec(get_now_utime());
		r = smap.get(key, (char*)&uii, uilen);
		e = get_usec(get_now_utime());
		n_get++;
		time_gets += (e - s);
		
		if(r)
		{
			s = get_usec(get_now_utime());
			smap.erase(key);
			e = get_usec(get_now_utime());
			n_erase++;
			time_erase += (e - s);
		}
	}
	ifs.close();
}

void test_smap_ex()
{
	tdl::shm sm;
	wbl::smap_ex smap;

	ofstream ofs;

	try {
		unsigned long shmsize = smap_ex::getSize(15000000, 15000000, 50);
		if(! sm.initialize(shmkey++, shmsize))
		{
			smap.create((char*)sm.get_p(), shmsize, 15000000, 15000000, 50);
			fill_smap(smap);
		}
	} catch(tdl::shm_ex& e) {
		cerr << e.what_hell() << endl;
		goto clean;
	} catch(runtime_error& e) {
		cerr << e.what() << endl;
		goto clean;
	}

	n_insert = n_touch = n_find = n_get = n_erase = 0;
	time_insert = time_touch = time_find = time_gets = time_erase = 0;

	test_smap_op(smap);

	ofs.open("moh_stat.dat", ios::out | ios::app);
	ofs << "=== wbl::smap_ex ===" << endl;
	print_into_statfile(ofs, "insert(): ", time_insert, n_insert);
	print_into_statfile(ofs, "find(): ", time_find, n_find);
	print_into_statfile(ofs, "touch_access():", time_touch, n_touch);
	print_into_statfile(ofs, "get(): ", time_gets, n_get);
	print_into_statfile(ofs, "erase(): ", time_erase, n_erase);
	ofs.close();

	cout << "test ok" << endl;

clean:
	// delete shm
	::shmctl(sm.get_id(), IPC_RMID, NULL);
}

int main(int argc, char* argv[])
{
	typedef mo_hash<uint32_t, uinfo, 16, tdl::hash, mohash_sp::accesstime_sort_policy> mohash_1_type;
	typedef mo_hash<uint32_t, uinfo, 32, tdl::hash, mohash_sp::accesstime_sort_policy> mohash_2_type;
	typedef mo_hash<uint32_t, uinfo, 50, tdl::hash, mohash_sp::accesstime_sort_policy> mohash_3_type;
	typedef mo_hash<uint32_t, uinfo, 16, tdl::hash, mohash_sp::createtime_sort_policy> mohash_4_type;
	typedef mo_hash<uint32_t, uinfo, 32, tdl::hash, mohash_sp::createtime_sort_policy> mohash_5_type;
	typedef mo_hash<uint32_t, uinfo, 50, tdl::hash, mohash_sp::createtime_sort_policy> mohash_6_type;
	typedef mo_hash<uint32_t, uinfo, 16, tdl::hash, mohash_sp::usecount_sort_policy> mohash_7_type;
	typedef mo_hash<uint32_t, uinfo, 32, tdl::hash, mohash_sp::usecount_sort_policy> mohash_8_type;
	typedef mo_hash<uint32_t, uinfo, 50, tdl::hash, mohash_sp::usecount_sort_policy> mohash_9_type;

	mohash_1_type moh1; mohash_2_type moh2; mohash_3_type moh3;
	mohash_4_type moh4; mohash_5_type moh5; mohash_6_type moh6;
	mohash_7_type moh7; mohash_8_type moh8; mohash_9_type moh9;

	string x = string(argv[1]);
	if(x == "moh1")
		test_moh(moh1, "ho=8, sp=at");
	else if(x == "moh2")
		test_moh(moh2, "ho=16, sp=at");
	else if(x == "moh3")
		test_moh(moh3, "ho=32, sp=at");
	else if(x == "moh4")
		test_moh(moh4, "ho=8, sp=ct");
	else if(x == "moh5")
		test_moh(moh5, "ho=16, sp=ct");
	else if(x == "moh6")
		test_moh(moh6, "ho=32, sp=ct");
	else if(x == "moh7")
		test_moh(moh7, "ho=8, sp=uc");
	else if(x == "moh8")
		test_moh(moh8, "ho=16, sp=uc");
	else if(x == "moh9")
		test_moh(moh9, "ho=32, sp=uc");
	else if(x == "map")
		test_map();
	else if(x == "smap")
		test_smap_ex();

	return 0;
}

